/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.hamacekj.dotaznikserver;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * Umožňuje třídění vstupních řádek protokolu podle id otázky.
 * Rozumí části definice protokolu. Dostává řádky surových dat protokolu, třídí je podle
 * jejich id a přeposílá je otázkám s odpovídajícím id. Všechny takové otázky se nejdřív
 * musí zaregistrovat pomocí
 * {@link ReplySorter#registerQuestion(cz.hamacekj.dotaznikserver.Question) registerQuestion}.
 * Pokud jde navíc o data s parametry, kompletuje nejdřív řádky a 
 * {@link Question otázce} posílá až zkompletovaná data.
 * @author Jan Hamáček
 */
public class ReplySorter implements DataRecievedListener {

    
    /**
     * Přijme jeden řádek protokolu, pokud je to řádek bez dat, 
     * hned ho odešle přes
     * {@link Question#addReply(cz.hamacekj.dotaznikserver.Connection, java.lang.String[]) Question.addReply()}.
     * jinak ho nejprve kompletuje.
     * @param c Spojení, ze kterého přišla odpověď.
     * @param o Odpověď protokolu 
     * (je na ní zavoláno {@link java.lang.Object#toString() toString())}.
     */
    @Override
    public synchronized void dataRecieved(Connection c, Object o) {
        if(!replyBuffer.containsKey(c)){
            replyBuffer.put(c, new LinkedList<String>());
        }
        List<String> buffer = replyBuffer.get(c);
        String reply = o.toString();
        if (".".equals(reply)){
            sendBuffer(c);
            return;
        }
        String[] arrReply = reply.split(" ", 4);
        try{
            switch(arrReply[0]){
                case "REPLY":
                    buffer.clear();
                    buffer.add(reply);
                    // textova odpoved je jedina, ktera obsahuje data. Pokud neni textova, muzu ji rovnou odeslat
                    // jinak musim kompletovat data
                    if(!"TEXT".equals(arrReply[1])){ 
                        sendBuffer(c);
                        return;
                    }
                    break;
                case "ERROR":
                    // s errorem nic nedelam. Jen vyprazdnim buffer.
                    buffer.clear();
                    break;
                default:
                    buffer.add(reply);
            }
        }catch(IndexOutOfBoundsException | NumberFormatException e){
            return;
        }
    }
    
    /**
     * Registruje q pro přijímání všech řádek protokolu se stejným id jako má ona.
     * Pokud je q s tímto id už registrována, vyhodí výjimku.
     * @param q Question, na které bude voláno addReply při přijetí řádky protokolu se jejím id.
     * @throws UsedIdException pokud je id otázky již registrované.
     */
    public void registerQuestion(Question q) throws UsedIdException{
        Integer id = q.getId();
        if(questions.containsKey(id)){
            throw new UsedIdException();
        }
        else{
            questions.put(id, q);
        }
    }
    
    /**
     * Zruší registraci id otázky q s q.
     * @param q Question k odregistrování.
     * @return true, pokud byla otázka odregistrována. False, pokud na id z q nebyla žádná hodnota.
     */
    public boolean unregisterQuestion(Question q){
        if(questions.remove(q.getId()) == null){
            return false;
        }else{
            return true;
        }
    }
    
    /**
     * Vrati vhodné volné id pro registraci další otázky.
     * @return Libovolné vhodné volné id.
     */
    public int getFreeId(){
        return ++maxindex;
    }
    
    private void sendBuffer(Connection c){
        List<String> buffer = replyBuffer.get(c);
        if (buffer == null){
            return;
        }
        try{
            String[] reply = buffer.toArray(new String[0]);
            String[] splitted = reply[0].split(" ", 4);
            Integer id = Integer.parseInt(splitted[2]);
            Question q = questions.get(id);
            q.addReply(c, reply);
        }catch(IndexOutOfBoundsException | NumberFormatException | NullPointerException e){}
        buffer.clear();
    }
    
    Map<Connection, List<String>> replyBuffer = new HashMap<>();
    Map<Integer, Question> questions = new HashMap<>();
    int maxindex = 0;
    
}
